
package cartagows.wsframework.wssecurity.policy;

import java.util.Iterator;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.neethi.Assertion;
import org.apache.neethi.builders.xml.XmlPrimtiveAssertion;

import cartagows.wsframework.wssecurity.policy.builders.AsymmetricBindingBuilder;
import cartagows.wsframework.wssecurity.policy.builders.EncryptedElementsBuilder;
import cartagows.wsframework.wssecurity.policy.builders.EncryptedPartsBuilder;
import cartagows.wsframework.wssecurity.policy.builders.RequiredElementsBuilder;
import cartagows.wsframework.wssecurity.policy.builders.SignedElementsBuilder;
import cartagows.wsframework.wssecurity.policy.builders.SignedPartsBuilder;
import cartagows.wsframework.wssecurity.policy.builders.SupportingTokensBuilder;
import cartagows.wsframework.wssecurity.policy.builders.SymmetricBindingBuilder;
import cartagows.wsframework.wssecurity.policy.builders.TransportBindingBuilder;
import cartagows.wsframework.wssecurity.policy.builders.WSS10Builder;
import cartagows.wsframework.wssecurity.policy.builders.WSS11Builder;
import cartagows.wsframework.wssecurity.policy.model.AsymmetricBinding;
import cartagows.wsframework.wssecurity.policy.model.Binding;
import cartagows.wsframework.wssecurity.policy.model.ContentEncryptedElements;
import cartagows.wsframework.wssecurity.policy.model.EncryptionToken;
import cartagows.wsframework.wssecurity.policy.model.Header;
import cartagows.wsframework.wssecurity.policy.model.InitiatorToken;
import cartagows.wsframework.wssecurity.policy.model.ProtectionToken;
import cartagows.wsframework.wssecurity.policy.model.RecipientToken;
import cartagows.wsframework.wssecurity.policy.model.RequiredElements;
import cartagows.wsframework.wssecurity.policy.model.SignatureToken;
import cartagows.wsframework.wssecurity.policy.model.SignedEncryptedElements;
import cartagows.wsframework.wssecurity.policy.model.SignedEncryptedParts;
import cartagows.wsframework.wssecurity.policy.model.SupportingToken;
import cartagows.wsframework.wssecurity.policy.model.SymmetricAsymmetricBindingBase;
import cartagows.wsframework.wssecurity.policy.model.SymmetricBinding;
import cartagows.wsframework.wssecurity.policy.model.TokenWrapper;
import cartagows.wsframework.wssecurity.policy.model.TransportBinding;
import cartagows.wsframework.wssecurity.policy.model.TransportToken;
import cartagows.wsframework.wssecurity.policy.model.Trust10;
import cartagows.wsframework.wssecurity.policy.model.Wss10;
import cartagows.wsframework.wssecurity.policy.model.Wss11;

public class PolicyBuilder {
    
    private static Log log = LogFactory.getLog(PolicyBuilder.class);
    

    public static PolicyData build(List topLevelAssertions)
            throws WSSPolicyException {
        
        PolicyData pd = new PolicyData();
        
        for (Iterator iter = topLevelAssertions.iterator(); iter.hasNext();) {
            Assertion assertion =  (Assertion) iter.next();
            
            if(assertion.getName().getLocalPart().equals("SymmetricBinding")){
            	SymmetricBindingBuilder sbb = new SymmetricBindingBuilder();
            	assertion = sbb.build(((XmlPrimtiveAssertion ) assertion).getValue(), null);
            	
            }
            if(assertion.getName().getLocalPart().equals("AsymmetricBinding")){
            	AsymmetricBindingBuilder abb = new AsymmetricBindingBuilder();
            	assertion = abb.build(((XmlPrimtiveAssertion ) assertion).getValue(), null);
            	
            }
            if(assertion.getName().getLocalPart().equals("TransportBinding")){
            	TransportBindingBuilder tbb = new TransportBindingBuilder();
            	assertion = tbb.build(((XmlPrimtiveAssertion ) assertion).getValue(), null);
            	
            }
            if(assertion.getName().getLocalPart().equals("Wss11")){
            	WSS11Builder w11b = new WSS11Builder();
            	assertion = w11b.build(((XmlPrimtiveAssertion ) assertion).getValue(), null);
            	
            }
            if(assertion.getName().getLocalPart().equals("Wss10")){
            	WSS10Builder w10b = new WSS10Builder();
            	assertion = w10b.build(((XmlPrimtiveAssertion ) assertion).getValue(), null);
            	
            }
            if(assertion.getName().getLocalPart().equals("SignedParts")){
            	SignedPartsBuilder spb = new SignedPartsBuilder();
            	assertion = spb.build(((XmlPrimtiveAssertion ) assertion).getValue(), null);
            }
            if(assertion.getName().getLocalPart().equals("EncryptedParts")){
            	EncryptedPartsBuilder epb = new EncryptedPartsBuilder();
            	assertion = epb.build(((XmlPrimtiveAssertion ) assertion).getValue(), null);
            }
            if(assertion.getName().getLocalPart().equals("SignedElements")){
            	SignedElementsBuilder seb = new SignedElementsBuilder();
            	assertion = seb.build(((XmlPrimtiveAssertion ) assertion).getValue(), null);
            }
            if(assertion.getName().getLocalPart().equals("EncryptedElements")){
            	EncryptedElementsBuilder eeb = new EncryptedElementsBuilder();
            	assertion = eeb.build(((XmlPrimtiveAssertion ) assertion).getValue(), null);
            }
            if(assertion.getName().getLocalPart().equals("RequiredElements")){
            	RequiredElementsBuilder reb = new RequiredElementsBuilder();
            	assertion = reb.build(((XmlPrimtiveAssertion ) assertion).getValue(), null);
            }
            if(assertion.getName().getLocalPart().equals("SupportingTokens") || assertion.getName().getLocalPart().equals("SignedSupportingTokens") || assertion.getName().getLocalPart().equals("EndorsingSupportingTokens") || assertion.getName().getLocalPart().equals("SignedEndorsingSupportingTokens") || assertion.getName().getLocalPart().equals("SignedEcryptedSupportingTokens") || assertion.getName().getLocalPart().equals("EndorsingEncryptedSupportingTokens") || assertion.getName().getLocalPart().equals("SignedEndorsingEncryptedSupportingTokens")){
            	SupportingTokensBuilder stb = new SupportingTokensBuilder();
            	assertion = stb.build(((XmlPrimtiveAssertion ) assertion).getValue(), null);
            }
            
            
            if (assertion instanceof Binding) {
                if (assertion instanceof SymmetricBinding) {
                    processSymmetricPolicyBinding((SymmetricBinding) assertion, pd);
                } else if(assertion instanceof AsymmetricBinding) {
                    processAsymmetricPolicyBinding((AsymmetricBinding) assertion, pd);
                } else {
                    processTransportBinding((TransportBinding) assertion, pd);
                }
                
            } else if (assertion instanceof Wss11) {
                processWSS11((Wss11) assertion, pd);
            } else if (assertion instanceof Wss10) {
                processWSS10((Wss10) assertion, pd);
            } else if (assertion instanceof SignedEncryptedElements) {
                processSignedEncryptedElements((SignedEncryptedElements) assertion,
                        pd);
            } else if (assertion instanceof SignedEncryptedParts) {
                processSignedEncryptedParts((SignedEncryptedParts) assertion, pd);
            } else if ( assertion instanceof RequiredElements) {   
                processRequiredElements((RequiredElements)assertion, pd);
            } else if (assertion instanceof ContentEncryptedElements) { 
                processContentEncryptedElements((ContentEncryptedElements) assertion, pd);
            }else if (assertion instanceof SupportingToken) {
                processSupportingTokens((SupportingToken) assertion, pd);
            } else if (assertion instanceof Trust10) {
                processTrust10((Trust10)assertion, pd);
            }  else {
                log.debug("Unknown top level PED found: "
                        + assertion.getClass().getName());
            }
        }
        
        return pd;
    }

    private static void processTransportBinding(TransportBinding binding, PolicyData pd) {
        binding(binding, pd);
        pd.setTransportBinding(true);
        TransportToken transportToken = binding.getTransportToken();
        if ( transportToken != null ) {
            pd.setTransportToken(transportToken.getTransportToken());
        }
    }

    private static void processTrust10(Trust10 trust10, PolicyData pd) {
        pd.setTrust10(trust10);
    }

    private static void processSymmetricPolicyBinding(
            SymmetricBinding symmBinding, PolicyData pd)
            throws WSSPolicyException {
        pd.setSymmetricBinding(true);
        binding(symmBinding, pd);
        symmAsymmBinding(symmBinding, pd);
        symmetricBinding(symmBinding, pd);
    }

    private static void processWSS10(Wss10 wss10, PolicyData pd) {
        pd.setWss10(wss10);
    }

    private static void processAsymmetricPolicyBinding(
            AsymmetricBinding binding, PolicyData pd)
            throws WSSPolicyException {
        pd.setAsymmetricBinding(true);
        binding(binding, pd);
        symmAsymmBinding(binding, pd);
        asymmetricBinding(binding, pd);
    }

    private static void processWSS11(Wss11 wss11, PolicyData pd) {
        pd.setSignatureConfirmation(wss11.isRequireSignatureConfirmation());
        pd.setWss11(wss11);
    }

    private static void processSignedEncryptedElements(
            SignedEncryptedElements see, PolicyData pd) {
        Iterator it = see.getXPathExpressions().iterator();
        if (see.isSignedElemets()) {
            while (it.hasNext()) {
                pd.setSignedElements((String) it.next());
            }
        } else {
            while (it.hasNext()) {
                pd.setEncryptedElements((String) it.next());
            }
        }
        pd.addDeclaredNamespaces(see.getDeclaredNamespaces());
    }

    private static void processSignedEncryptedParts(SignedEncryptedParts sep,
            PolicyData pd) {
        Iterator it = sep.getHeaders().iterator();
        if (sep.isSignedParts()) {
            pd.setSignBody(sep.isBody());
            pd.setSignAttachments(sep.isAttachments());
            while (it.hasNext()) {
                Header header = (Header) it.next();
                pd.addSignedPart(header.getNamespace(), header.getName());
            }
        } else {
            pd.setEncryptBody(sep.isBody());
            pd.setEncryptAttachments(sep.isAttachments());
            while (it.hasNext()) {
                Header header = (Header) it.next();
                pd.setEncryptedParts(header.getNamespace(), header.getName(),"Header");
            }
        }
    }
    
    private static void processContentEncryptedElements(ContentEncryptedElements cee,
            PolicyData pd) {
        
        Iterator it = cee.getXPathExpressions().iterator();     
        while (it.hasNext()) {
            pd.setContentEncryptedElements((String) it.next());
        }
        pd.addDeclaredNamespaces(cee.getDeclaredNamespaces());
    }

    private static void processRequiredElements(RequiredElements req,
            PolicyData pd) {
        
        Iterator it = req.getXPathExpressions().iterator();     
        while (it.hasNext()) {
            pd.setRequiredElements((String) it.next());
        }
        pd.addDeclaredNamespaces(req.getDeclaredNamespaces());
    }

    private static void binding(Binding binding, PolicyData pd) {
        pd.setLayout(binding.getLayout().getValue());
        pd.setIncludeTimestamp(binding.isIncludeTimestamp());
        pd.setAlgorithmSuite(binding.getAlgorithmSuite());
    }

    private static void symmAsymmBinding(
            SymmetricAsymmetricBindingBase binding, PolicyData pd) {
        pd.setEntireHeadersAndBodySignatures(binding
                .isEntireHeadersAndBodySignatures());
        pd.setProtectionOrder(binding.getProtectionOrder());
        pd.setSignatureProtection(binding.isSignatureProtection());
        pd.setTokenProtection(binding.isTokenProtection());
        pd.setAlgorithmSuite(binding.getAlgorithmSuite());
    }

    private static void symmetricBinding(SymmetricBinding binding,
            PolicyData pd) throws WSSPolicyException {
        Assertion token = binding.getProtectionToken();
        
        if (token != null) {
            pd.setProtectionToken(((ProtectionToken)token).getProtectionToken());
        } else {
            Assertion encrToken = binding.getEncryptionToken();
            Assertion sigToken = binding.getSignatureToken();
            if (token == null && sigToken == null) {
                throw new WSSPolicyException("Symmetric binding should have a Protection token or" +
                		                " both Signature and Encryption tokens defined");
            }
            pd.setEncryptionToken(
                    ((EncryptionToken) encrToken).getEncryptionToken());
            pd.setSignatureToken(((SignatureToken) sigToken).getSignatureToken());
        }
    }

    private static void asymmetricBinding(AsymmetricBinding binding,
            PolicyData pd) throws WSSPolicyException {
        TokenWrapper tokWrapper = binding.getRecipientToken();
        TokenWrapper tokWrapper1 = binding.getInitiatorToken();
        if (tokWrapper == null || tokWrapper1 == null) {
            throw new WSSPolicyException("Asymmetric binding should have both Initiator and " +
            		                                                "Recipient tokens defined");
        }
        pd.setRecipientToken(((RecipientToken) tokWrapper).getReceipientToken());
        pd.setInitiatorToken(((InitiatorToken) tokWrapper1).getInitiatorToken());
    }

    private static void processSupportingTokens(SupportingToken token,
            PolicyData pd) throws WSSPolicyException {
        pd.setSupportingTokens(token);
    }
    
   
}
